#include "ThreadPool.h"
#include "WorkThread.h"
#include "Task.h"
#include <unistd.h>

#include <iostream>

using std::cout;
using std::endl;

ThreadPool::ThreadPool(size_t threadNum, size_t queSize)
: _threadNum(threadNum)
, _queSize(queSize)
/* , _threads(10)// */
, _taskQue(_queSize)
, _isExit(false)
{
    //使用的是预留空间
    _threads.reserve(_threadNum);
}

ThreadPool::~ThreadPool()
{

}

//线程池的启动与停止
void ThreadPool::start()
{
    for(size_t idx = 0; idx < _threadNum; ++idx)
    {
        //1、创建工作线程
        unique_ptr<Thread> up(new WorkThread(*this));

        //2、将所有的工作线程放在vector存起来
        //unique_ptr作为容器元素的时候，只能传右值
        _threads.push_back(std::move(up));
        /* _threads.push_back(unique_ptr<Thread>(new WorkThread(*this))); */
    }

    //3、遍历vector，将所有的工作线程运行起来
    for(auto &th : _threads)
    {
        th->start();//将所有的工作线程运行起来
    }
}

void ThreadPool::stop()
{
    //只要任务队列中有任务，就不能让工作线程退出
    while(!_taskQue.empty())
    {
        sleep(1);
    }
    _isExit = true;//标识线程池的退出

    //唤醒所有在_notEmpty条件变量上的线程
    /* _notEmpty.notifyAll(); */
    _taskQue.wakeup();

    //1、遍历vector，将所有的工作线程停止运行
    for(auto &th : _threads)
    {
        th->stop();//将所有的工作线程停止运行
    }
}

//添加任务
void ThreadPool::addTask(Task *ptask)
{
    if(ptask)
    {
        _taskQue.push(ptask);
    }
}

//获取任务
Task *ThreadPool::getTask()
{
    return _taskQue.pop();
}

//线程池交给工作线程做的事件
//问题分析：为何线程池退不出来？
//A：工作线程在执行doTask函数的时候，会判断_isExit是不是为
//false，如果是false的话，就会在while循环中。工作线程需要
//到任务队列中拿任务，也就是执行getTask，如果有任务就会
//获取任务并且调用process执行任务。同时主线程会等待着
//子线程，也就是主线程会执行stop函数。当主线程执行的比较
//快的时候，那么有足够的时间将_isExit设置为true，那么
//子线程在执行完process函数之后，就不能进入到while循环中
//也就是不会再执行getTask拿任务，而因为没有任务导致阻塞。
//也就是子线程执行任务比较慢;但是如果子线程通过getTask
//拿到任务之后，执行任务process的速率比主线程执行的速率
//要快的话，那么可能在主线程执行stop中，还没有来得及将
//_isExit设置为true，子线程就已经进入了doTask的while循环
//而此时，任务队列中没有任务,那么子线程就会阻塞
//也就是在_notEmpty条件变量上睡眠
void ThreadPool::doTask()
{
    //只要线程池不退出，那就应该让工作线程一直执行任务
    while(!_isExit)
    {
        //1、首先要获取任务
        Task *ptask = getTask();
        if(ptask)
        {
            //2、其次再执行任务
            ptask->process();//线程池交给工作线程做的任务
            /* sleep(3); */
        }
        else
        {
            cout << "nullptr == ptask" << endl;
        }
    }
}
